package controller

import (
	"context"
	"errors"
	"net"
	"net/http"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"lab.com/conf"
	"lab.com/define"
	"lab.com/define/errorcode"
)

type cBase struct {
	APIBase
}

// Base .
var Base = cBase{}

// NoRoute no
func (api *cBase) NoRoute(c *gin.Context) {

	api.Resp(c, "", errorcode.New(http.StatusNotFound, http.StatusText(http.StatusNotFound), errors.New(c.Request.URL.String()+" not found")))

	c.Abort()

}

// Pong returns server status
func (api *cBase) Pong(c *gin.Context) {
	api.Resp(c, "pong", nil)
}

// APIBase .
type APIBase struct {
}

// API .
var API = APIBase{}

// Resp used for generally response to client
func (api *APIBase) Resp(c *gin.Context, payload interface{}, err error) {
	if err == nil {
		api.successResponse(c, http.StatusOK, payload)
	} else {
		api.errorResponse(c, http.StatusOK, err)
	}

}

// RespWithStatus used for generally response to client with http status
func (api *APIBase) RespWithStatus(c *gin.Context, statusCode int, payload interface{}, err error) {

	if err == nil {
		api.successResponse(c, statusCode, payload)
	} else {
		api.errorResponse(c, statusCode, err)
	}
}

// RespAbortWithStatus abort the handle chain and response with http status
func (api *APIBase) RespAbortWithStatus(c *gin.Context, statusCode int, err error) {

	c.Abort()

	api.errorResponse(c, statusCode, err)
}

// RespNeedAuthentication Response 401 Need Authenticate
func (api *APIBase) RespNeedAuthentication(c *gin.Context) {

	api.RespAbortWithStatus(c, http.StatusOK,
		errorcode.New(errorcode.ErrAuthenticationRequired, "登录状态无效，请重新登录", nil))
}

// RespRoleNeedPermission Response 401 role Need permission
func (api *APIBase) RespRoleNeedPermission(c *gin.Context) {

	api.RespAbortWithStatus(c, http.StatusOK,
		errorcode.New(errorcode.ErrRolePermissionRequired, errorcode.RolePermissionRequiredMsg, nil))
}

// RespUserNeedRole Response 401 user need role
func (api *APIBase) RespUserNeedRole(c *gin.Context) {

	api.RespAbortWithStatus(c, http.StatusOK,
		errorcode.New(errorcode.ErrParams, errorcode.UserRoleRequiredMsg, nil))
}

func (api *APIBase) successResponse(c *gin.Context, statusCode int, payload interface{}) {
	if payload == nil {
		payload = map[string]string{}
	}
	ctx := c.Request.Context()
	params, _ := c.GetRawData()
	logrus.WithFields(logrus.Fields{
		"header":     c.Request.Header,
		"clientIp":   c.ClientIP(),
		"remoteIp":   c.RemoteIP(),
		"params":     string(params),
		"requestUri": c.Request.RequestURI,
	}).Info("【请求日志】")
	logrus.WithFields(logrus.Fields{
		"reqVer":    api.requestVersion(c),
		"code":      errorcode.Success,
		"message":   errorcode.SuccessMsg,
		"data":      payload,
		"traceID":   ctx.Value(define.TranceKey("trace_id")),
		"timestamp": time.Now().UnixMilli(),
	}).Info("【响应日志】")
	c.JSON(statusCode, define.Response{
		ReqVer:    api.requestVersion(c),
		Code:      errorcode.Success,
		Message:   errorcode.SuccessMsg,
		Data:      payload,
		TraceID:   ctx.Value(define.TranceKey("trace_id")),
		Timestamp: time.Now().UnixMilli(),
	})
}

func (api *APIBase) errorResponse(c *gin.Context, statusCode int, err error) {
	ctx := c.Request.Context()
	params, _ := c.GetRawData()
	logrus.WithFields(logrus.Fields{
		"header":     c.Request.Header,
		"clientIp":   c.ClientIP(),
		"remoteIp":   c.RemoteIP(),
		"params":     string(params),
		"requestUri": c.Request.RequestURI,
	}).Info("【请求日志】")
	logrus.WithFields(logrus.Fields{
		"reqVer":    api.requestVersion(c),
		"code":      api.guessErrorCode(err),
		"message":   err.Error(),
		"data":      map[string]string{},
		"traceID":   ctx.Value(define.TranceKey("trace_id")),
		"timestamp": time.Now().UnixMilli(),
	}).Info("【响应日志】")
	c.JSON(statusCode, define.Response{
		ReqVer:    api.requestVersion(c),
		Code:      api.guessErrorCode(err),
		Message:   api.guessErrorMessage(ctx, err),
		Data:      map[string]string{},
		TraceID:   ctx.Value(define.TranceKey("trace_id")),
		Timestamp: time.Now().UnixMilli(),
	})
}

func (api *APIBase) requestVersion(c *gin.Context) int {

	var requestVersion string
	if c.Request.Method == "POST" {
		requestVersion = c.PostForm("req_ver")
		if requestVersion == "" {
			requestVersion = "0"
		}

	} else {
		requestVersion = c.DefaultQuery("req_ver", "0")
	}

	version, err := strconv.Atoi(requestVersion)
	if err != nil {
		return 0
	}

	return version
}

func (api *APIBase) guessErrorCode(err error) int {

	if apiError, ok := err.(*errorcode.APIError); ok {
		return apiError.Code
	}

	if err, ok := err.(net.Error); ok && err.Timeout() {
		return errorcode.ErrServiceRespTimeout
	}

	return errorcode.ErrBizLogic
}

func (api *APIBase) guessErrorMessage(ctx context.Context, err error) string {

	if err != nil {
		conf.AppLog.WithContext(ctx).WithFields(logrus.Fields{
			"error": err,
		}).Warn()
	}

	if apiError, ok := err.(*errorcode.APIError); ok {
		return apiError.Msg
	}

	if err, ok := err.(net.Error); ok && err.Timeout() {
		return "依赖服务请求超时"
	}

	return errorcode.DefaultErrorMsg
}


func (api *APIBase) RespJwtInvalid(c *gin.Context) {

	api.RespAbortWithStatus(c, http.StatusOK,
		errorcode.New(errorcode.ErrAuthenticationRequired, "您的账号已过期或被禁用，请重新登录。如有问题请联系管理员！", nil))
}